#include "shared_ptr.hpp"
#include <gtest/gtest.h>
#include <string>

/**
 * @brief 辅助结构：记录构造与析构次数，用于验证释放逻辑。
 */
struct Tracker {
    static inline int constructions = 0;
    static inline int destructions  = 0;

    Tracker()  { ++constructions; }
    ~Tracker() { ++destructions;  }

    static void reset() {
        constructions = 0;
        destructions  = 0;
    }
};

/**
 * @brief 测试 1：默认构造的 SharedPtr 应为空，引用计数为 0。
 */
TEST(SharedPtrTest, DefaultConstruction) {
    SharedPtr<int> ptr;
    EXPECT_EQ(ptr.get(), nullptr);  // get() 返回裸指针，应为空
    EXPECT_EQ(ptr.use_count(), 0u); // 空指针的引用计数为 0
    EXPECT_FALSE(ptr);              // bool 转换结果应为 false
}

/**
 * @brief 测试 2：make_shared 创建对象后，可正常访问并统计引用计数。
 */
TEST(SharedPtrTest, MakeSharedAndDereference) {
    auto sp = make_shared<std::string>("hello");
    EXPECT_TRUE(sp);                // 非空
    EXPECT_EQ(sp.use_count(), 1u);  // 刚创建只有 1 个拥有者
    EXPECT_EQ(*sp, "hello");        // 解引用读取字符串
    EXPECT_TRUE(sp.unique());       // use_count == 1 => unique()
}

/**
 * @brief 测试 3：拷贝构造/赋值应增加引用计数，共享拥有权。
 */
TEST(SharedPtrTest, CopyIncrementsCount) {
    auto sp1 = make_shared<int>(42);   // 初始 use_count = 1
    SharedPtr<int> sp2 = sp1;          // 拷贝构造，use_count -> 2
    SharedPtr<int> sp3(sp2);           // 再拷贝一次，use_count -> 3

    EXPECT_EQ(sp1.use_count(), 3u);
    EXPECT_EQ(sp2.use_count(), 3u);
    EXPECT_EQ(sp3.use_count(), 3u);

    EXPECT_EQ(*sp1, 42);               // 三者指向同一对象
    EXPECT_EQ(*sp2, 42);
    EXPECT_EQ(*sp3, 42);
}

/**
 * @brief 测试 4：移动构造/赋值应转移拥有权，原对象置空。
 */
TEST(SharedPtrTest, MoveTransfersOwnership) {
    auto sp1 = make_shared<int>(7);
    SharedPtr<int> sp2 = std::move(sp1);  // 移动构造：sp1 被置空

    EXPECT_FALSE(sp1);                    // sp1 不再持有资源
    EXPECT_EQ(sp1.use_count(), 0u);       // 引用计数为 0
    EXPECT_TRUE(sp2);                     // sp2 接管资源
    EXPECT_EQ(sp2.use_count(), 1u);
    EXPECT_EQ(*sp2, 7);

    SharedPtr<int> sp3;
    sp3 = std::move(sp2);                 // 移动赋值
    EXPECT_FALSE(sp2);
    EXPECT_TRUE(sp3);
    EXPECT_EQ(sp3.use_count(), 1u);
    EXPECT_EQ(*sp3, 7);
}

/**
 * @brief 测试 5：reset() 应正确释放对象（引用计数归零时析构）。
 */
TEST(SharedPtrTest, ResetReleasesResource) {
    Tracker::reset();                     // 清零统计数据
    {
        auto sp = make_shared<Tracker>(); // constructions +1
        EXPECT_EQ(Tracker::constructions, 1);
        EXPECT_EQ(Tracker::destructions, 0);
        EXPECT_EQ(sp.use_count(), 1u);

        sp.reset();                       // 主动释放引用
        EXPECT_FALSE(sp);
        EXPECT_EQ(sp.use_count(), 0u);
    }
    EXPECT_EQ(Tracker::destructions, 1);   // 对象应被析构一次
}

/**
 * @brief 测试 6：自定义删除器能被调用（例如释放非 new 资源）。
 */
TEST(SharedPtrTest, CustomDeleterIsInvoked) {
    bool deleter_called = false;

    auto deleter = [&deleter_called](int* p) {
        deleter_called = true;            // 记录删除器被调用
        delete p;                         // 仍然使用 delete 释放
    };

    {
        SharedPtr<int> sp(new int(99), deleter);
        EXPECT_TRUE(sp);
        EXPECT_EQ(*sp, 99);
        EXPECT_EQ(sp.use_count(), 1u);
    }                                     // 作用域结束触发析构
    EXPECT_TRUE(deleter_called);          // 删除器应被执行
}

/**
 * @brief 测试 7：自赋值操作不会破坏引用计数和资源。
 */
TEST(SharedPtrTest, SelfAssignmentSafe) {
    auto sp = make_shared<int>(5);
    sp = sp;                              // 自赋值
    EXPECT_TRUE(sp);
    EXPECT_EQ(sp.use_count(), 1u);
    EXPECT_EQ(*sp, 5);
}

/**
 * @brief 测试 8：swap 操作应交换拥有的对象和引用计数。
 */
TEST(SharedPtrTest, SwapExchangesOwners) {
    auto sp1 = make_shared<int>(1);
    auto sp2 = make_shared<int>(2);

    sp1.swap(sp2);                        // 交换内部指针和控制块

    EXPECT_EQ(*sp1, 2);                   // sp1 现在指向原 sp2 的对象
    EXPECT_EQ(*sp2, 1);
    EXPECT_EQ(sp1.use_count(), 1u);       // 仍然各自独占
    EXPECT_EQ(sp2.use_count(), 1u);
}
